function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
function _defineProperties(target, props) {
    for(var i = 0; i < props.length; i++){
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}
function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}
//﻿
// Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///<reference path='formatting.ts' />
var Formatting1;
(function(Formatting) {
    var Indenter = /*#__PURE__*/ function() {
        "use strict";
        function Indenter(logger, tree, snapshot, languageHostIndentation, editorOptions, firstToken, smartIndent) {
            _classCallCheck(this, Indenter);
            this.logger = logger;
            this.tree = tree;
            this.snapshot = snapshot;
            this.languageHostIndentation = languageHostIndentation;
            this.editorOptions = editorOptions;
            this.firstToken = firstToken;
            this.smartIndent = smartIndent;
            this.indentationBag = new IndentationBag(this.snapshot);
            this.scriptBlockBeginLineNumber = -1;
            this.offsetIndentationDeltas = new Dictionary_int_int(); // text offset -> indentation delta
            // by default the root (program) has zero indendation
            this.tree.Root.SetIndentationOverride("");
            this.ApplyScriptBlockIndentation(this.languageHostIndentation, this.tree);
            this.FillInheritedIndentation(this.tree);
        }
        _createClass(Indenter, [
            {
                key: "GetIndentationEdits",
                value: function GetIndentationEdits(token, nextToken, node, sameLineIndent) {
                    if (this.logger.information()) {
                        this.logger.log("GetIndentationEdits(" + "t1=[" + token.Span.startPosition() + "," + token.Span.endPosition() + "], " + "t2=[" + (nextToken == null ? "null" : nextToken.Span.startPosition() + "," + nextToken.Span.endPosition()) + "]" + ")");
                    }
                    var result = this.GetIndentationEditsWorker(token, nextToken, node, sameLineIndent);
                    if (this.logger.information()) {
                        for(var i = 0; i < result.count(); i++){
                            var edit = result.get(i);
                            this.logger.log("edit: minChar=" + edit.position + ", limChar=" + (edit.position + edit.length) + ", text=\"" + TypeScript.stringToLiteral(edit.replaceWith, 30) + "\"");
                        }
                    }
                    return result;
                }
            },
            {
                key: "GetIndentationEditsWorker",
                value: function GetIndentationEditsWorker(token, nextToken, node, sameLineIndent) {
                    var result = new List_TextEditInfo();
                    var indentationInfo = null;
                    // This handles the case:
                    //      return (
                    //              function() {
                    //              })
                    // The given function's node indicates that the function starts directly after "return (".
                    // In this case, we adjust the span to point to the function keyword.
                    // The same applies to objects and arrays.
                    // The reason this is done inside the Indenter is because it only affects indentation behavior.
                    // It's also done in ParseTree when we traverse up the tree because we don't have the 
                    // tokens for nodes outside the span we are formatting.
                    this.AdjustStartOffsetIfNeeded(token, node);
                    // Don't adjust indentation on the same line of a script block
                    if (this.scriptBlockBeginLineNumber == token.lineNumber()) {
                        return result;
                    }
                    // Don't indent multi-line strings
                    if (!sameLineIndent && this.IsMultiLineString(token)) {
                        return result;
                    }
                    // Special cases for the tokens that don't show up in the tree, such as curly braces and comments
                    indentationInfo = this.GetSpecialCaseIndentation(token, node);
                    if (indentationInfo == null) {
                        //// For anything else
                        // Get the indentation level only from the node that starts on the same offset as the token
                        // otherwise the token is not meant to be indented
                        while(!node.CanIndent() && node.Parent != null && token.Span.span.start() == node.Parent.AuthorNode.Details.StartOffset)node = node.Parent;
                        if (node.CanIndent() && token.Span.span.start() == node.AuthorNode.Details.StartOffset) {
                            indentationInfo = node.GetEffectiveIndentation(this);
                        } else {
                            //// Special cases for anything else that is not in the tree and should be indented
                            // check for label (identifier followed by a colon)
                            if (token.Token == AuthorTokenKind.atkIdentifier && nextToken != null && nextToken.Token == AuthorTokenKind.atkColon) {
                                // This will make the label on the same level as the surrounding function/block
                                // ex: 
                                // {
                                //      statement;
                                //      label:
                                //          statement;
                                // }
                                indentationInfo = node.GetEffectiveChildrenIndentation(this);
                            } else {
                                //// Move the token the same indentation-delta that moved its indentable parent
                                //// For example:
                                ////    var a,
                                ////        b;
                                //// The declaration 'b' would remain under 'a' even if 'var' got indented.
                                indentationInfo = this.ApplyIndentationDeltaFromParent(token, node);
                            }
                        }
                    }
                    // Get the indent edit from the indentation info
                    if (indentationInfo != null) {
                        var edit = this.GetIndentEdit(indentationInfo, token.Span.startPosition(), sameLineIndent);
                        if (edit != null) {
                            this.RegisterIndentation(edit, sameLineIndent);
                            result.add(edit);
                            // multi-line comments, apply delta indentation to all the other lines
                            if (token.Token == AuthorTokenKind.atkComment) {
                                var commentEdits = this.GetCommentIndentationEdits(token);
                                commentEdits.foreach(function(item) {
                                    result.add(item);
                                });
                            }
                        }
                    }
                    return result;
                }
            },
            {
                key: "GetCommentIndentationEdits",
                value: function GetCommentIndentationEdits(token) {
                    var result = new List_TextEditInfo();
                    if (token.Token != AuthorTokenKind.atkComment) return result;
                    var commentLastLineNumber = this.snapshot.GetLineNumberFromPosition(token.Span.endPosition());
                    if (token.lineNumber() == commentLastLineNumber) return result;
                    var commentFirstLineIndentationDelta = this.GetIndentationDelta(token.Span.startPosition(), null);
                    if (commentFirstLineIndentationDelta != undefined) {
                        for(var line = token.lineNumber() + 1; line <= commentLastLineNumber; line++){
                            var lineStartPosition = this.snapshot.GetLineFromLineNumber(line).startPosition();
                            var lineIndent = this.GetLineIndentationForOffset(lineStartPosition);
                            var commentIndentationInfo = this.ApplyIndentationDelta2(lineIndent, commentFirstLineIndentationDelta);
                            if (commentIndentationInfo != null) {
                                var tokenStartPosition = lineStartPosition + lineIndent.length;
                                var commentIndentationEdit = this.GetIndentEdit(commentIndentationInfo, tokenStartPosition, false);
                                if (commentIndentationEdit != null) {
                                    result.add(commentIndentationEdit);
                                }
                            }
                        }
                    }
                    return result;
                }
            },
            {
                key: "GetSpecialCaseIndentation",
                value: function GetSpecialCaseIndentation(token, node) {
                    var indentationInfo = null;
                    switch(token.Token){
                        case AuthorTokenKind.atkLCurly:
                            indentationInfo = this.GetSpecialCaseIndentationForLCurly(node);
                            return indentationInfo;
                        case AuthorTokenKind.atkElse:
                        case AuthorTokenKind.atkRBrack:
                            indentationInfo = node.GetNodeStartLineIndentation(this);
                            return indentationInfo;
                        case AuthorTokenKind.atkRCurly:
                            // if '}' is for a body-block, get indentation based on its parent.
                            if (node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkBlock && node.AuthorNode.EdgeLabel == AuthorParseNodeEdge.apneBody) node = node.Parent;
                            indentationInfo = node.GetNodeStartLineIndentation(this);
                            return indentationInfo;
                        case AuthorTokenKind.atkWhile:
                            if (node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkDoWhile) {
                                indentationInfo = node.GetNodeStartLineIndentation(this);
                                return indentationInfo;
                            }
                            return null;
                        case AuthorTokenKind.atkSColon:
                            return this.GetSpecialCaseIndentationForSemicolon(token, node);
                        case AuthorTokenKind.atkComment:
                            return this.GetSpecialCaseIndentationForComment(token, node);
                        default:
                            return indentationInfo;
                    }
                }
            },
            {
                key: "GetSpecialCaseIndentationForLCurly",
                value: function GetSpecialCaseIndentationForLCurly(node) {
                    var indentationInfo = null;
                    if (node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkFncDecl || node.AuthorNode.EdgeLabel == AuthorParseNodeEdge.apneThen || node.AuthorNode.EdgeLabel == AuthorParseNodeEdge.apneElse) {
                        // flushed with the node (function & if)
                        indentationInfo = node.GetNodeStartLineIndentation(this);
                        return indentationInfo;
                    } else if (node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkObject && !node.CanIndent()) {
                        // if the open curly belongs to a non-indented object, do nothing here.
                        return null;
                    }
                    // effective identation of the block
                    indentationInfo = node.GetEffectiveIndentation(this);
                    return indentationInfo;
                }
            },
            {
                key: "GetSpecialCaseIndentationForSemicolon",
                value: function GetSpecialCaseIndentationForSemicolon(token, node) {
                    var indentationInfo = null;
                    if (this.smartIndent) {
                        indentationInfo = node.GetEffectiveChildrenIndentation(this);
                        return indentationInfo;
                    } else {
                        // Indent all semicolons except the ones that belong to the for statement parts (initalizer, condition, itnrement)
                        if (node.AuthorNode.Details.Kind != AuthorParseNodeKind.apnkFor) {
                            // The passed node is actually either the program or the list because semicolon doesn't belong
                            // to any statement in the tree, though the statement extends up to the semicolon position.
                            // To find the correct statement, we look for the adjacent node on the left of the semicolon.
                            var semiColonStartSpan = new Span(token.Span.startPosition(), 0);
                            node = ParseTree.FindCommonParentNode(semiColonStartSpan, semiColonStartSpan, node);
                            indentationInfo = node.GetEffectiveChildrenIndentation(this);
                            return indentationInfo;
                        }
                    }
                    return null;
                }
            },
            {
                key: "GetSpecialCaseIndentationForComment",
                value: function GetSpecialCaseIndentationForComment(token, node) {
                    var indentationInfo = null;
                    // Only indent line comment and the first line of block comment
                    var twoCharSpan = token.Span.Intersection(new Span(token.Span.startPosition(), 2));
                    if (twoCharSpan != null && (twoCharSpan.GetText() == "//" || twoCharSpan.GetText() == "/*")) {
                        while(node.ChildrenIndentationDelta == null && node.Parent != null)node = node.Parent;
                        if (this.CanIndentComment(token, node)) {
                            indentationInfo = node.GetEffectiveChildrenIndentationForComment(this);
                        } else {
                            indentationInfo = this.ApplyIndentationDeltaFromParent(token, node);
                        }
                    }
                    return indentationInfo;
                }
            },
            {
                key: "CanIndentComment",
                value: function CanIndentComment(token, node) {
                    switch(node.AuthorNode.Details.Kind){
                        case AuthorParseNodeKind.apnkProg:
                        case AuthorParseNodeKind.apnkBlock:
                        case AuthorParseNodeKind.apnkSwitch:
                        case AuthorParseNodeKind.apnkCase:
                        case AuthorParseNodeKind.apnkDefaultCase:
                        case AuthorParseNodeKind.apnkIf:
                        case AuthorParseNodeKind.apnkFor:
                        case AuthorParseNodeKind.apnkForIn:
                        case AuthorParseNodeKind.apnkWhile:
                        case AuthorParseNodeKind.apnkWith:
                        case AuthorParseNodeKind.apnkDoWhile:
                        case AuthorParseNodeKind.apnkObject:
                            return true;
                        case AuthorParseNodeKind.apnkFncDecl:
                            // Comments before arguments are not indented.
                            // This code doesn't cover the cases of comment after the last argument or 
                            // when there are no arguments. Though this is okay since the only case we care about is:
                            // function foo(/* test */ a,
                            //              /* test */ b)
                            var result = true;
                            var children = ParseNodeExtensions.FindChildrenWithEdge(node, AuthorParseNodeEdge.apneArgument);
                            children.foreach(function(argumentNode) {
                                if (token.Span.startPosition() < argumentNode.AuthorNode.Details.StartOffset) result = false;
                            });
                            return result;
                    }
                    return false;
                }
            },
            {
                key: "ApplyScriptBlockIndentation",
                value: function ApplyScriptBlockIndentation(languageHostIndentation, tree) {
                    if (languageHostIndentation == null || tree.StartNodeSelf == null) return;
                    var scriptBlockIndentation = this.ApplyIndentationLevel(languageHostIndentation, 1);
                    //TypeScript: Projection snapshots not supported
                    // Disconnect the sibling node if it belongs to a different script block
                    //IProjectionSnapshot projectionSnapshot = this.snapshot as IProjectionSnapshot;
                    //if (projectionSnapshot != null)
                    //{
                    //    // Get script block spans.
                    //    foreach (SnapshotSpan sourceSpan in projectionSnapshot.GetSourceSpans())
                    //    {
                    //        // Map the spans to the JavaScript buffer.
                    //        ReadOnlyCollection<Span> spans = projectionSnapshot.MapFromSourceSnapshot(sourceSpan);
                    //        Debug.Assert(spans.Count == 1, string.Format(CultureInfo.InvariantCulture, "Unexpected span count of {0}.", spans.Count));
                    //        if (spans.Count > 0)
                    //        {
                    //            Span span = spans.First();
                    //            // If the "self" node is the first root-level node in a script block, then remove the start node.
                    //            if (span.Contains(tree.StartNodethis.AuthorNode.Details.StartOffset))
                    //            {
                    //                this.scriptBlockBeginLineNumber = projectionSnapshot.GetLineNumberFromPosition(span.Start);
                    //                if (tree.StartNodePreviousSibling.HasValue)
                    //                {
                    //                    int siblingStartOffset = tree.StartNodePreviousSibling.Value.Details.StartOffset;
                    //                    // Don't consider sibling in these cases:
                    //                    // 1. The sibling belongs to another script block
                    //                    // 2. The sibling is on the same line of the script block
                    //                    if (!span.Contains(siblingStartOffset) || projectionSnapshot.GetLineNumberFromPosition(siblingStartOffset) == this.scriptBlockBeginLineNumber)
                    //                    {
                    //                        tree.StartNodePreviousSibling = null;
                    //                    }
                    //                }
                    //                break;
                    //            }
                    //        }
                    //    }
                    //}
                    // The root is the program.
                    tree.Root.SetIndentationOverride(scriptBlockIndentation);
                }
            },
            {
                key: "GetIndentEdit",
                value: function GetIndentEdit(indentInfo, tokenStartPosition, sameLineIndent) {
                    var indentText = this.ApplyIndentationLevel(indentInfo.Prefix, indentInfo.Level);
                    if (sameLineIndent) {
                        return new TextEditInfo(tokenStartPosition, 0, indentText);
                    } else {
                        var snapshotLine = this.snapshot.GetLineFromPosition(tokenStartPosition);
                        var currentIndentSpan = new Span(snapshotLine.startPosition(), tokenStartPosition - snapshotLine.startPosition());
                        var currentIndentText = this.snapshot.GetText(currentIndentSpan);
                        if (currentIndentText !== indentText) {
                            if (this.logger.debug()) {
                                // Verify that currentIndentText is all whitespaces
                                for(var i = 0, len = currentIndentText.length; i < len; i++){
                                    var c = currentIndentText.charCodeAt(i);
                                    if (!StringUtils.IsWhiteSpace(c)) {
                                        Debug.Fail("Formatting error: Will remove user code when indenting the line: " + snapshotLine.getText());
                                        break;
                                    }
                                }
                            }
                            return new TextEditInfo(currentIndentSpan.start(), currentIndentSpan.length(), indentText);
                        }
                    }
                    return null;
                }
            },
            {
                key: "ApplyIndentationLevel",
                value: function ApplyIndentationLevel(existingIndentation, level) {
                    var indentSize = this.editorOptions.IndentSize;
                    var tabSize = this.editorOptions.TabSize;
                    var convertTabsToSpaces = this.editorOptions.ConvertTabsToSpaces;
                    if (level < 0) {
                        if (StringUtils.IsNullOrEmpty(existingIndentation)) return "";
                        var totalIndent = 0;
                        StringUtils.foreach(existingIndentation, function(c) {
                            if (c == '\t') totalIndent += tabSize;
                            else totalIndent++;
                        });
                        totalIndent += level * indentSize;
                        if (totalIndent < 0) return "";
                        else return this.GetIndentString(null, totalIndent, tabSize, convertTabsToSpaces);
                    }
                    var totalIndentSize = level * indentSize;
                    return this.GetIndentString(existingIndentation, totalIndentSize, tabSize, convertTabsToSpaces);
                }
            },
            {
                key: "GetIndentString",
                value: function GetIndentString(prefix, totalIndentSize, tabSize, convertTabsToSpaces) {
                    var tabString = convertTabsToSpaces ? StringUtils.create(' ', tabSize) : "\t";
                    var text = "";
                    if (!StringUtils.IsNullOrEmpty(prefix)) text += prefix;
                    var pos = 0;
                    // fill first with tabs
                    while(pos <= totalIndentSize - tabSize){
                        text += tabString;
                        pos += tabSize;
                    }
                    // fill the reminder with spaces
                    while(pos < totalIndentSize){
                        text += ' ';
                        pos++;
                    }
                    return text;
                }
            },
            {
                key: "ApplyIndentationDeltaFromParent",
                value: function ApplyIndentationDeltaFromParent(token, node) {
                    var indentationInfo = null;
                    var indentableParent = node;
                    while(indentableParent != null && !indentableParent.CanIndent())indentableParent = indentableParent.Parent;
                    if (indentableParent != null && indentableParent.AuthorNode.Details.Kind != AuthorParseNodeKind.apnkProg) {
                        var parentIndentationDeltaSize = this.GetIndentationDelta(indentableParent.AuthorNode.Details.StartOffset, token.Span.startPosition());
                        if (parentIndentationDeltaSize !== undefined) {
                            indentationInfo = this.ApplyIndentationDelta1(token.Span.startPosition(), parentIndentationDeltaSize);
                        }
                    }
                    return indentationInfo;
                }
            },
            {
                key: "ApplyIndentationDelta1",
                value: function ApplyIndentationDelta1(tokenStartPosition, delta) {
                    // Get current indentation
                    var snapshotLine = this.snapshot.GetLineFromPosition(tokenStartPosition);
                    var currentIndentSpan = new Span(snapshotLine.startPosition(), tokenStartPosition - snapshotLine.startPosition());
                    var currentIndent = this.snapshot.GetText(currentIndentSpan);
                    // Calculate new indentation from current-indentation and delta
                    return this.ApplyIndentationDelta2(currentIndent, delta);
                }
            },
            {
                key: "ApplyIndentationDelta2",
                value: function ApplyIndentationDelta2(currentIndent, delta) {
                    if (delta == 0) return null;
                    var currentIndentSize = Indenter.GetIndentSizeFromIndentText(currentIndent, this.editorOptions);
                    var newIndentSize = currentIndentSize + delta;
                    if (newIndentSize < 0) {
                        newIndentSize = 0;
                    }
                    var newIndent = this.GetIndentString(null, newIndentSize, this.editorOptions.TabSize, this.editorOptions.ConvertTabsToSpaces);
                    if (newIndent != null) {
                        return new IndentationInfo(newIndent, 0);
                    }
                    return null;
                }
            },
            {
                key: "GetIndentationDelta",
                value: function GetIndentationDelta(tokenStartPosition, childTokenStartPosition /*?*/ ) {
                    Debug.Assert(childTokenStartPosition !== undefined, "Error: caller must pass 'null' for undefined position");
                    var indentationDeltaSize = this.offsetIndentationDeltas.GetValue(tokenStartPosition);
                    if (indentationDeltaSize === null) {
                        var indentEditInfo = this.indentationBag.FindIndent(tokenStartPosition);
                        // No recorded indentation, return null
                        if (indentEditInfo == null) return null;
                        var origIndentText = this.snapshot.GetText(new Span(indentEditInfo.OrigIndentPosition, indentEditInfo.OrigIndentLength()));
                        var newIndentText = indentEditInfo.Indentation();
                        var origIndentSize = Indenter.GetIndentSizeFromText(origIndentText, this.editorOptions, /*includeNonIndentChars*/ true);
                        var newIndentSize = Indenter.GetIndentSizeFromIndentText(newIndentText, this.editorOptions);
                        // Check the child's position whether it's before the parent position
                        // if so indent the child based on the first token on the line as opposed to the parent position
                        //
                        // Example of relative to parent (not line), relative indentation should be "4 (newIndentSize) - 9 (indentSize up to for) = -5"
                        //
                        // if (1) { for (i = 0; i < 10;       =>          if (1) {
                        //                      i++) {                       for (i = 0; i < 10;
                        //                                                               i++) {
                        //
                        // Example of relative to line, relative indentation should be "4 (newIndentSize) - 0 (indentSize up to if) = 4"
                        //
                        // if (1) { for (i = 0; i < 10;      =>          if (1) {
                        //     i++) {                                        for (i = 0; i < 10;
                        //                                                       i++) {
                        if (childTokenStartPosition !== null) {
                            var childTokenLineStartPosition = this.snapshot.GetLineFromPosition(childTokenStartPosition).startPosition();
                            var childIndentText = this.snapshot.GetText(new Span(childTokenLineStartPosition, childTokenStartPosition - childTokenLineStartPosition));
                            var childIndentSize = Indenter.GetIndentSizeFromIndentText(childIndentText, this.editorOptions);
                            if (childIndentSize < origIndentSize) origIndentSize = Indenter.GetIndentSizeFromIndentText(origIndentText, this.editorOptions);
                        }
                        indentationDeltaSize = newIndentSize - origIndentSize;
                        this.offsetIndentationDeltas.Add(tokenStartPosition, indentationDeltaSize);
                    }
                    return indentationDeltaSize;
                }
            },
            {
                key: "FillInheritedIndentation",
                value: function FillInheritedIndentation(tree) {
                    var offset = -1;
                    var indentNode = null;
                    if (tree.StartNodeSelf != null) {
                        if (!this.smartIndent && tree.StartNodePreviousSibling !== null && tree.StartNodeSelf.AuthorNode.Label == 0 && tree.StartNodePreviousSibling.Label == 0) {
                            indentNode = tree.StartNodeSelf;
                            offset = tree.StartNodePreviousSibling.Details.StartOffset;
                            // In case the sibling node is on the same line of a parent node, ex:
                            //      case 1: a++;
                            //          break;
                            // In this example, the sibling of break is a++ but a++ is on the same line of its parent.
                            var lineNum = this.snapshot.GetLineNumberFromPosition(offset);
                            var node = indentNode;
                            while(node.Parent != null && this.snapshot.GetLineNumberFromPosition(node.Parent.AuthorNode.Details.StartOffset) == lineNum){
                                node = node.Parent;
                                if (node.CanIndent()) {
                                    indentNode = node;
                                    indentNode.IndentationDelta = 0;
                                }
                            }
                        } else {
                            var parent;
                            // Otherwise base on parent indentation.
                            if (this.smartIndent) {
                                // in smartIndent the self node is the parent node since it's the closest node to the new line
                                // ... unless in case if the startNodeSelf represents the firstToken then we need to choose its parent
                                parent = tree.StartNodeSelf;
                                while(parent != null && parent.AuthorNode.Details.StartOffset == this.firstToken.Span.startPosition())parent = parent.Parent;
                            } else {
                                // Get the parent that is really on a different line from the self node
                                var startNodeLineNumber = this.snapshot.GetLineNumberFromPosition(tree.StartNodeSelf.AuthorNode.Details.StartOffset);
                                parent = tree.StartNodeSelf.Parent;
                                while(parent != null && startNodeLineNumber == this.snapshot.GetLineNumberFromPosition(parent.AuthorNode.Details.StartOffset)){
                                    parent = parent.Parent;
                                }
                            }
                            // The parent node to take its indentation is the first parent that has indentation.
                            while(parent != null && !parent.CanIndent()){
                                parent = parent.Parent;
                            }
                            // Skip Program since it has no indentation
                            if (parent != null && parent.AuthorNode.Details.Kind != AuthorParseNodeKind.apnkProg) {
                                offset = parent.AuthorNode.Details.StartOffset;
                                indentNode = parent;
                            }
                        }
                    }
                    if (indentNode != null) {
                        var indentOverride = this.GetLineIndentationForOffset(offset);
                        // Set the indentation on all the siblings to be the same as indentNode
                        if (!this.smartIndent && tree.StartNodePreviousSibling !== null && indentNode.Parent != null) {
                            ParseNodeExtensions.GetChildren(indentNode.Parent).foreach(function(sibling) {
                                if (sibling !== indentNode) {
                                    if (sibling.CanIndent()) sibling.SetIndentationOverride(indentOverride);
                                }
                            });
                        }
                        // Set the indent override string on the indent node and on every parent (on different line) after adjusting the indent by the negative delta
                        var lastDelta = 0;
                        var lastLine = this.snapshot.GetLineNumberFromPosition(indentNode.AuthorNode.Details.StartOffset);
                        do {
                            var currentLine = this.snapshot.GetLineNumberFromPosition(indentNode.AuthorNode.Details.StartOffset);
                            if (lastLine != currentLine) {
                                lastLine = currentLine;
                                indentOverride = this.ApplyIndentationLevel(indentOverride, -lastDelta);
                                lastDelta = 0;
                            }
                            if (indentNode.CanIndent()) {
                                indentNode.SetIndentationOverride(indentOverride);
                                lastDelta = indentNode.IndentationDelta;
                            }
                            indentNode = indentNode.Parent;
                        }while (indentNode != null)
                    }
                }
            },
            {
                key: "GetLineIndentationForOffset",
                value: function GetLineIndentationForOffset(offset) {
                    var indentationEdit;
                    // First check if we already have indentation info in our indentation bag
                    indentationEdit = this.indentationBag.FindIndent(offset);
                    if (indentationEdit != null) {
                        return indentationEdit.Indentation();
                    } else {
                        // Otherwise, use the indentation from the textBuffer
                        var line = this.snapshot.GetLineFromPosition(offset);
                        var lineText = line.getText();
                        var index = 0;
                        while(index < lineText.length && (lineText.charAt(index) == ' ' || lineText.charAt(index) == '\t')){
                            ++index;
                        }
                        return lineText.substr(0, index);
                    }
                }
            },
            {
                key: "RegisterIndentation",
                value: function RegisterIndentation(indent, sameLineIndent) {
                    var indentationInfo = null;
                    if (sameLineIndent) {
                        // Consider the original indentation from the beginning of the line up to the indent position (or really the token position)
                        var lineStartPosition = this.snapshot.GetLineFromPosition(indent.Position).startPosition();
                        var lineIndentLength = indent.Position - lineStartPosition;
                        indentationInfo = IndentationEditInfo.create2(indent.Position, indent.ReplaceWith, lineStartPosition, lineIndentLength);
                    } else {
                        indentationInfo = new IndentationEditInfo(indent);
                    }
                    this.indentationBag.AddIndent(indentationInfo);
                }
            },
            {
                key: "RegisterIndentation2",
                value: function RegisterIndentation2(position, indent) {
                    this.RegisterIndentation(new TextEditInfo(position, 0, indent), false);
                }
            },
            {
                key: "AdjustStartOffsetIfNeeded",
                value: function AdjustStartOffsetIfNeeded(token, node) {
                    if (token == null) return;
                    var updateStartOffset = false;
                    switch(token.Token){
                        case AuthorTokenKind.atkFunction:
                            updateStartOffset = node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkFncDecl;
                            break;
                        case AuthorTokenKind.atkLCurly:
                            updateStartOffset = node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkObject;
                            break;
                        case AuthorTokenKind.atkLBrack:
                            updateStartOffset = node.AuthorNode.Details.Kind == AuthorParseNodeKind.apnkArray;
                            break;
                    }
                    if (updateStartOffset) {
                        ParseNodeExtensions.SetNodeSpan(node, token.Span.startPosition(), node.AuthorNode.Details.EndOffset);
                    }
                }
            },
            {
                key: "IsMultiLineString",
                value: function IsMultiLineString(token) {
                    return token.tokenID === TypeScript.TokenID.StringLiteral && this.snapshot.GetLineNumberFromPosition(token.Span.endPosition()) > this.snapshot.GetLineNumberFromPosition(token.Span.startPosition());
                }
            }
        ], [
            {
                key: "GetIndentSizeFromIndentText",
                value: function GetIndentSizeFromIndentText(indentText, editorOptions) {
                    return GetIndentSizeFromText(indentText, editorOptions, /*includeNonIndentChars:*/ false);
                }
            },
            {
                key: "GetIndentSizeFromText",
                value: function GetIndentSizeFromText(text, editorOptions, includeNonIndentChars) {
                    var indentSize = 0;
                    for(var i = 0; i < text.length; i++){
                        var c = text.charAt(i);
                        if (c == '\t') indentSize = indentSize + editorOptions.TabSize - indentSize % editorOptions.TabSize;
                        else if (c == ' ') indentSize += 1;
                        else {
                            if (includeNonIndentChars) indentSize += 1;
                            else break;
                        }
                    }
                    return indentSize;
                }
            }
        ]);
        return Indenter;
    }();
    Formatting.Indenter = Indenter;
})(Formatting1 || (Formatting1 = {
}));
